package br.com.starcode.threadswithtransactionexample; import java.sql.Connection; import java.sql.ResultSet; import java.sql.Statement; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import javax.sql.XAConnection; import javax.transaction.SystemException; import javax.transaction.Transaction; import javax.transaction.xa.XAResource; public class AtomikosDAO { /** * Reset table content with 5 rows to be used each one for a different thread */ public static void resetTable() { try { Connection c = AtomikosDataSource.getDS().getConnection(); Statement s = c.createStatement(); s.executeUpdate("delete from teste"); s.executeUpdate("insert into teste (id, nome, processado) values (1, 'Item 1', null)"); s.executeUpdate("insert into teste (id, nome, processado) values (2, 'Item 2', null)"); s.executeUpdate("insert into teste (id, nome, processado) values (3, 'Item 3', null)"); s.executeUpdate("insert into teste (id, nome, processado) values (4, 'Item 4', null)"); s.executeUpdate("insert into teste (id, nome, processado) values (5, 'Item 5', null)"); s.close(); c.close(); } catch (Throwable e) { e.printStackTrace(); } } /** * Count haw many rows are with ok flag * @return */ public static int countOk() { try { Connection c = AtomikosDataSource.getDS().getConnection(); Statement s = c.createStatement(); ResultSet rs = s.executeQuery("select count(1) from teste where processado = 'ok'"); if (rs.next()) { return rs.getInt(1); } rs.close(); s.close(); c.close(); } catch (Throwable e) { e.printStackTrace(); } return 0; } /** * Processing thread. * Tries to update the row with "ok" */ private static class Processamento implements Callable<Integer> { private int id; private boolean falhar; private Transaction transaction; public Processamento(int id, boolean falhar, Transaction transaction) { this.falhar = falhar; this.transaction = transaction; this.id = id; } public Integer call() throws Exception { if (falhar) { throw new RuntimeException("Falhou inesperadamente!"); } //enlist xa connection XAConnection xac = AtomikosDataSource.getDS().getXaDataSource().getXAConnection(); synchronized (transaction) { transaction.enlistResource(xac.getXAResource()); } //normal execution, update row with OK Connection c = xac.getConnection(); Statement s = c.createStatement(); s.executeUpdate("update teste set processado = 'ok' where id = " + id); s.close(); c.close(); //delist xa connection synchronized (transaction) { transaction.delistResource(xac.getXAResource(), XAResource.TMSUCCESS); } return id; } } /** * Starts 5 threads. Each thread update a row of the TEST table with OK. * @param falhar If true, the last thread will throw an exception. * @return Total threads that result in success */ public static int processar(boolean falhar) { int ok = 0; Transaction transaction = null; try { //start transaction AtomikosDataSource.getTM().begin(); transaction = AtomikosDataSource.getTM().getTransaction(); //create thread pool ExecutorService executor = Executors.newFixedThreadPool(5); List<Callable<Integer>> processos = new ArrayList<Callable<Integer>>(); //create 5 threads, passing the main transaction as argument for (int i = 0; i < 5; i++) { processos.add(new Processamento(i + 1, i == 4 && falhar, transaction)); } //execute threads and wait List<Future<Integer>> futures = executor.invokeAll(processos); //count the result; get() will fail if thread threw an exception Throwable ex = null; for (Future<Integer> future : futures) { try { int threadId = future.get(); System.out.println("Thread " + threadId + " sucesso!"); ok++; } catch (Throwable e) { ex = e; } } if (ex != null) { throw ex; } //finish transaction normally transaction.commit(); } catch (Throwable e) { e.printStackTrace(); try { //try to rollback if (transaction != null) { AtomikosDataSource.getTM().rollback(); } } catch (IllegalStateException e1) { e1.printStackTrace(); } catch (SecurityException e1) { e1.printStackTrace(); } catch (SystemException e1) { e1.printStackTrace(); } } return ok; } }